package gobelinmaker.client;

import com.sun.scenario.scenegraph.SGGroup;
import com.sun.scenario.scenegraph.SGNode;
import gobelinmaker.data.Wool;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.Iterator;
import java.util.LinkedList;

/**
 * Fonal komponens alaposztálya.
 *
 * @author imruf84
 */
public class WoolComponent extends SGGroup {

    /**
     * Fonal.
     */
    private final Wool wool;
    /**
     * Fonalhoz tartozó összes csomó.
     */
    private final SGGroup allStitchesNode = new SGGroup();
    /**
     * Fonalhoz tartozó csomók (az utolsó kivételével).
     */
    private final SGGroup stitchesNode = new SGGroup();
    /**
     * Utolsó öltés tárolója.
     */
    private final SGGroup lastStitchNode = new SGGroup();
    /**
     * Fonal tárolója.
     */
    private final SGGroup woolNode = new SGGroup();
    /**
     * Felső szegmensek tárolója.
     */
    private final SGGroup frontWoolNode = new SGGroup();
    /**
     * Alsó szegmensek tárolója.
     */
    private final SGGroup backWoolNode = new SGGroup();

    /**
     * Konstruktor.
     *
     * @param wool fonal
     */
    public WoolComponent(Wool wool) {
        this.wool = wool;

        this.add(this.woolNode);
        this.woolNode.add(this.backWoolNode);
        this.woolNode.add(this.frontWoolNode);
        this.add(this.allStitchesNode);
        allStitchesNode.add(this.stitchesNode);
        allStitchesNode.add(this.lastStitchNode);
    }

    /**
     * Fonal lekérdezése.
     *
     * @return fonal
     */
    public Wool getWool() {
        return this.wool;
    }

    /**
     * Összes öltés csomópontjának a lekérdezése.
     *
     * @return öltések csomópontja
     */
    public SGGroup getAllStitchesNode() {
        return this.allStitchesNode;
    }

    /**
     * Öltések csomópontjának a lekérdezése.
     *
     * @return öltések csomópontja
     */
    public SGGroup getStitchesNode() {
        return this.stitchesNode;
    }

    /**
     * Utolsó öltés csomópontjának a lekérdezése.
     *
     * @return utolsó öltés csomópontja
     */
    public SGGroup getLastStitchNode() {
        return this.lastStitchNode;
    }

    /**
     * Csomók láthatóságának megadása.
     *
     * @param v láthatóság
     */
    public void setStitchesVisible(boolean v) {
        this.getStitchesNode().setVisible(v);
        this.getLastStitchNode().setVisible(v);
        this.getAllStitchesNode().setVisible(v);
    }

    /**
     * Fonal csomópontjának a lekérdezése.
     *
     * @return fonal csomópontja
     */
    public SGGroup getWoolNode() {
        return this.woolNode;
    }

    /**
     * Hátsó szálak csomópontjának a lekérdezése.
     *
     * @return hátsó szálak csomópontja
     */
    public SGGroup getBackWoolNode() {
        return this.backWoolNode;
    }

    /**
     * Fonalszakasz hozzáadása a fonalhoz.
     */
    public void appendWoolSegment() {
        LinkedList<StitchComponent> scl = this.getStitchComponents();
        StitchComponent sc = scl.getLast();

        double l = HoleComponent.OUTER_SIZE / 4;

        // Felső szál.
        Rectangle2D r = sc.getBounds();
        Point2D p = new Point2D.Double(r.getCenterX(), r.getCenterY());
        Point2D from = new Point2D.Double(p.getX() - l, p.getY() + l);
        Point2D to = new Point2D.Double(p.getX() + l, p.getY() - l);

        WoolFrontSegmentComponent wfs = new WoolFrontSegmentComponent(this, sc, sc.getStitch().isReversed() ? to : from, sc.getStitch().isReversed() ? from : to);
        this.frontWoolNode.add(wfs);

        // Ha csak egy csomó van (azaz nincs előző), akkor kilépünk.
        if (1 == scl.size()) {
            return;
        }

        // Egyébként létrehozzuk az alsó szálat.
        StitchComponent scp = scl.get(scl.size() - 2);
        Rectangle2D rp = scp.getBounds();
        Point2D pp = new Point2D.Double(rp.getCenterX(), rp.getCenterY());
        Point2D top = scp.getStitch().isReversed() ? new Point2D.Double(pp.getX() - l, pp.getY() + l) : new Point2D.Double(pp.getX() + l, pp.getY() - l);
        Point2D fromp = sc.getStitch().isReversed() ? to : from;

        WoolBackSegmentComponent wbs = new WoolBackSegmentComponent(this, sc, top, fromp);
        this.backWoolNode.add(wbs);
    }

    /**
     * Fonalrész eltávolítása.
     */
    public void removeLastWoolSegment() {
        // Ha van gyermeke az utolsó felső szálnak, akkro töröljük.
        if (0 < this.frontWoolNode.getChildren().size()) {
            this.frontWoolNode.remove(this.frontWoolNode.getChildren().size() - 1);
        }
        // Ha van gyermeke az utolsó alsó szálnak, akkro töröljük.
        if (0 < this.backWoolNode.getChildren().size()) {
            this.backWoolNode.remove(this.backWoolNode.getChildren().size() - 1);
        }
    }

    /**
     * Fonal színének a frissítése.
     */
    public void updateWoolColor() {
        Iterator<SGNode> it = this.frontWoolNode.getChildren().iterator();
        while (it.hasNext()) {
            WoolFrontSegmentComponent wfs = (WoolFrontSegmentComponent) it.next();
            wfs.updateColor();
        }

        it = this.backWoolNode.getChildren().iterator();
        while (it.hasNext()) {
            WoolBackSegmentComponent wbs = (WoolBackSegmentComponent) it.next();
            wbs.updateColor();
        }
    }

    /**
     * Öltések lekérdezése.
     *
     * @return öltések
     */
    public LinkedList<StitchComponent> getStitchComponents() {

        // TODO: - csak 2 réteget néz végig, javítani általános esetre + hatékonyabb algoritmus kell
        LinkedList<StitchComponent> result = new LinkedList<>();
        Iterator<SGNode> it = this.getAllStitchesNode().getChildren().iterator();
        while (it.hasNext()) {
            SGGroup n = (SGGroup) it.next();
            Iterator<SGNode> it2 = n.getChildren().iterator();
            while (it2.hasNext()) {
                SGNode n2 = it2.next();
                if (n2 instanceof StitchComponent) {
                    StitchComponent sc = (StitchComponent) n2;
                    result.add(sc);
                }
            }
        }

        return result;
    }

    /**
     * Az utólsó öltés lekérdezése.
     *
     * @return utolsó öltés
     */
    public StitchComponent getLastStitchComponent() {
        if (!(0 < this.lastStitchNode.getChildren().size())) {
            return null;
        }

        return (StitchComponent) this.lastStitchNode.getChildren().get(this.lastStitchNode.getChildren().size() - 1);
    }

    /**
     * Utolsó előtti öltés lekérdezése.
     *
     * @return utolsó előtti öltés
     */
    public StitchComponent getBeforeLastStitchComponent() {
        if (!(0 < this.stitchesNode.getChildren().size())) {
            return null;
        }

        return (StitchComponent) this.stitchesNode.getChildren().get(this.stitchesNode.getChildren().size() - 1);
    }

    /**
     * Fonalrész irányának megfordítása.
     *
     * @param sc öltés
     */
    public void reverseWoolSegment(StitchComponent sc) {
        Iterator<SGNode> it = this.frontWoolNode.getChildren().iterator();
        WoolSegmentComponent wsc = null;
        while (it.hasNext()) {
            WoolSegmentComponent lwsc = (WoolSegmentComponent) it.next();
            if (sc.equals(lwsc.getStitchComponent())) {
                wsc = lwsc;
                break;
            }
        }

        // Ha nem találtunk semmit, akkor kilépünk.
        if (null == wsc) {
            return;
        }

        // Egyébként mehet a munka.
        // Öltés megfrodítása (ha már eleve fordítva van).
        Point2D p = wsc.getFrom();
        wsc.setFrom(wsc.getTo());
        wsc.setTo(p);
        wsc.updatePositions();

        // Fonalrész sorszáma.
        int index = this.frontWoolNode.getChildren().indexOf(wsc);

        // Ha van előző fonalrész...
        if (0 < index) {
            WoolSegmentComponent wscp = (WoolSegmentComponent) this.backWoolNode.getChildren().get(index - 1);
            wscp.setTo(wsc.getFrom());
            wscp.updatePositions();
        }

        // Ha an következő fonalrész...
        if (index < this.frontWoolNode.getChildren().size() - 1) {
            WoolSegmentComponent wscn = (WoolSegmentComponent) this.backWoolNode.getChildren().get(index);
            wscn.setFrom(wsc.getTo());
            wscn.updatePositions();
        }

    }
}
